一个涉及Flex,Keep-Alive, PHP的bug排除实录

Categories: Flex; Tagged with: ; @ August 14th, 2010 23:41

Flex端依次向PHP客户端发送两个Request, 由于php端处理第一个Request时存在bug, 由于Keep-Alive, 第二个Request会先接收第一个Request的残局, 造成第二个Request在FireFox下出现异常.

涉及到的内容: PHP, Http Header, Flex URLLoader URLRequest, Keep-Alive, Apache AccessLog, FireFox, Firebug(除了Flex我比较熟之外其他基本上都是懂一点, 所以下面说辞可能会与具体语言/工具有所出入).

起因非常简单: php端一个while的condition没有随着循环而++.

详细描述:

为了测试服务器速度, 我们用Flex + PHP写了一个测试程序, 测试一组服务器的上传下载速度, Debug时很稳定, 如图:
image

IE下测试效果很好, 但在Build后放在FireFox上测试, Firefox的pulugin-container就占到若干G内存. 以前我只写过卡死浏览器的程序, 还不曾写过直接死机的程序, 结果昨晚dubug时竟然搞死我的电脑一次.

Flex端会通过URLLoader及URLRequest与PHP进行交互, 我们先从服务器读取1M的数据, 计算速度, 然后将数据发送到php端保存. 整个客户端的过程就是: Download, Report.

PHP端收到Download请求后, 会生成一个1M的文件返回到Flex端; 收到Report请求后会保存速度等参数, 并会返回ip所在地.

在使用FireFox进行测速时, 发现在Report后进入假死状态, 直到flash crash或浏览器死掉, 于是使用Firebug查看Request情况, 如图:
image

非常不可思议的是发现Report中返回的数据竟然超大, 而且越来越大, 毫无停止的迹象. (后来该用FireFox浏览器进行Debug, 在监听Report.ProgressEvent时发现 bytesTotal也在不断的长大)

费解啊费解, 后来发现在Flex端如果在Download结束后, 暂停上2秒(及以上), 再进行report就会正常report并返回ip所在地.

怀疑可能是Report Action处理有问题? 于是查看了Apahce的AccessLog, 发现只有Download的Access记录, 而无Report的Access记录. 我干啊..

于是猜想:  可能是跟浏览器的Keep-Alive有关

仔细排查了PHP端代码, 发现在生成文件的一个while中, 参数没有++, 导致在Download时, 虽然打算创建1M的文件, 但实际上while停不下一直会增大文件…

但Download的Header里明确了文件大小信息:

header("Content-Length: ".(string)($downloadSize));

所以浏览器跟URLRequest都只会下载1M的数据,  但服务器仍在while循环, 文件仍在不断变大….

猜想可能是由于FireFox的keep-alive, 在Report请求时, 发现TCP连接中仍有活动的connection,  于是先继续接受TCP已接收的数据, 也就是Download的1M数据之外的数据, 于是Report开始后实际上还是在下载1M之外未完的数据.  由于1M之外的数据读不完, 下了大量数据, 于是关掉FireFox或plugin-container进程, Firebug中看到的Report Request实际仍没有发送到服务器端, 于是造成了Report收拾Download残局的现状.

如此折腾下来, 看来只需修复php端bug就好, 果然搞定.

重复验证:

php端处理DownloadAction代码中, 创建完毕1M数据后, echo ‘liguoliang.com’;

也就是这个几个字符会继续发送到客户机, 但不会被浏览器处理(Header中定义了文件下载大小为1M),  因此多余的’liguoliang.com’ 会在第二次Request后首先从客户机读取, 然后再向服务器端请求(由于此前文件不断变大, 所以等不到收拾完Download Request残局就crash了).

果然, 查看Apache的Access log, 正常, 有两个记录, 再看Firebug:

image

果然,  先收拾了上一把的残局, 搞的下一把都异常了.

这个bug出现的条件:

测试服务器在本机, 所以操作都很快, 眨眼就下完1M数据, 然后会keep-alive,  report Request会收拾前一个有问题的Request的残局. 如果测试服务器在外部, 或两个Request间隔大于2秒(前一个结束到后一个开始)第二个Request会直接去请求服务器, 而不是先吃剩饭, 造成异常. 

<->



// Proudly powered by Apache, PHP, MySQL, WordPress, Bootstrap, etc,.